package com.synergy.stockflow.config;

import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

/**
 * HikariCP 连接池监控组件
 *
 * <p>该组件通过定时任务定期输出 HikariCP 连接池的运行状态，包括活跃连接、空闲连接、等待线程等关键指标，
 * 用于监控数据库连接使用情况，及时发现连接泄漏、连接打满等潜在问题。</p>
 *
 * <p>由于 HikariCP 采用懒加载机制，连接池在首次数据库访问前不会初始化，因此在无用户使用时，
 * 可能出现连接数为 0 或 HikariPoolMXBean 为 null 的情况，属于正常行为。</p>
 *
 * <p>建议配合 {warmup} 在应用启动时进行数据库预热，以确保连接池尽早初始化。</p>
 *
 * <p>关键监控指标说明：</p>
 * <ul>
 *   <li><b>活跃连接（active）</b>：当前正在被使用的连接数</li>
 *   <li><b>空闲连接（idle）</b>：空闲但可复用的连接数</li>
 *   <li><b>等待线程（awaiting）</b>：因连接池已满而阻塞等待的线程数（>0 需警惕）</li>
 * </ul>
 *
 * @author YHCinaMoll
 * @since 2025-08-07
 */
@Component
@Log4j2
public class HikariMonitor {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private DataSource dataSource;

    /**
     * 在应用启动时执行一次简单查询，触发 HikariCP 连接池初始化
     * 避免因懒加载导致监控数据长时间为 null
     */
    @PostConstruct
    public void warmup() {
        try {
            jdbcTemplate.queryForObject("SELECT 1", Integer.class);
            log.info("✅ 数据库连接预热成功，HikariCP 已初始化");
        } catch (Exception e) {
            log.warn("⚠️ 数据库预热失败，可能数据库未启动或网络问题", e);
        }
    }

    /**
     * 定时任务：每 5 分钟执行一次，输出 HikariCP 连接池状态日志
     *
     * <p>监控内容包括：</p>
     * <ul>
     *   <li>活跃连接数、空闲连接数、总连接数</li>
     *   <li>等待获取连接的线程数</li>
     *   <li>连接池名称、最大连接数</li>
     * </ul>
     *
     * <p>告警逻辑：</p>
     * <ul>
     *   <li>当有线程等待连接时，输出 WARN 日志</li>
     *   <li>当活跃连接数达到最大连接数时，提示连接池已满</li>
     * </ul>
     *
     * <p>注意：若连接池尚未初始化（无数据库访问），将输出提示信息，不视为异常。</p>
     */
    @Scheduled(fixedDelay = 5 * 60 * 1000) // 每5分钟一次
    public void logConnectionPoolStatus() {
        log.debug("📊 定时任务触发：检查 HikariCP 连接池状态");

        // 1. 检查 DataSource 是否注入
        if (dataSource == null) {
            log.warn("❌ DataSource 未注入，请检查配置！");
            return;
        }

        // 2. 检查是否为 HikariDataSource
        if (!(dataSource instanceof HikariDataSource)) {
            log.warn("⚠️ 当前数据源不是 HikariDataSource，类型: {}", dataSource.getClass().getName());
            return;
        }

        HikariDataSource hikariDS = (HikariDataSource) dataSource;

        // 3. 尝试获取 HikariPoolMXBean（可能为 null，如果连接池未初始化）
        HikariPoolMXBean poolBean = hikariDS.getHikariPoolMXBean();
        if (poolBean == null) {
            log.info("🟡 HikariCP 连接池尚未初始化（无数据库访问），状态: active=0, idle=0, awaiting=0");
            return;
        }

        try {
            int active = poolBean.getActiveConnections();
            int idle = poolBean.getIdleConnections();
            int total = active + idle;
            int awaiting = poolBean.getThreadsAwaitingConnection();
            int maxPoolSize = hikariDS.getMaximumPoolSize();

            // 输出到日志
            log.info("📊 HikariCP 连接池状态 | " +
                            "活跃: {} | " +
                            "空闲: {} | " +
                            "总计: {} | " +
                            "等待: {} | " +
                            "最大连接: {} | " +
                            "池名: {}",
                    active, idle, total, awaiting, maxPoolSize, hikariDS.getPoolName());

            // 警告：有线程在等待连接（可能连接打满）
            if (awaiting > 0) {
                log.warn("⚠️ 有 {} 个线程正在等待数据库连接！", awaiting);
            }

            // 警告：连接池已满（接近或达到最大）
            if (active >= maxPoolSize) {
                log.warn("🔥 连接池已满（活跃={} >= 最大={}），请检查是否存在连接泄漏或并发过高！", active, maxPoolSize);
            }

            // 提示：空闲连接过多（可优化配置）
            if (idle > 5 && idle == maxPoolSize) {
                log.info("💡 当前空闲连接较多（{}），可检查 minimum-idle 配置是否合理", idle);
            }

        } catch (Exception e) {
            log.error("❌ 获取 HikariCP 状态失败", e);
        }
    }
}